iT邦幫忙

2022 iThome 鐵人賽

DAY 4
0
自我挑戰組

30天 Ruby on Rails 挑戰系列 第 4

Rails 30天挑戰第四天 CRUD(二)

  • 分享至 

  • xImage
  •  

現在基礎的model、controller、view已經建立起來了,開始正式操作。
在終端機輸入$rails s打開server,rails在瀏覽器的預設是http://localhost:3000

打開後可以看到一群舉手歡呼的人,代表server是正常打開的。

index

進入到我們設定的路徑http://localhost:3000/books

會出現錯誤說在BooksController缺少index這個action。
如果沒有的話,就把它做出來。
在BooksController先寫上index這個action

class BooksController < ApplicationController
    
  def index
  end
    
end

index這個action主管的是這個books的目錄
回到瀏覽器

這個錯誤是index找不到與其對應的view,還是那句話,如果沒有的話,就把它做出來。
到app/views/books建立index.html.erb
可以在上面寫上一段code

<h1>Hello World!!</h1>


可以看到我們剛剛寫的Hello World!!被渲染出來

New

接下來寫新增功能,在index.html.erb寫上一段超連結聯到http://localhost:3000/books/new
在一般HTML我們寫超連結會這樣寫:

<a href="http://localhost:3000/books/new">新增</a>

在Rails我們可以用route給我們的Perfix去寫超連結。

可以看到new的Perfix是new_book,我們可以用link_to去寫

<%= link_to "新增",new_book_path %>

點下新增這個超連結


可以看到預期的畫面,缺少new這個action。沒有就做出來,
順便在view/books建立new.html.erb

class BooksController < ApplicationController
    
  def index
  end
  
  def new
  end
    
end

再來是寫上表單

<form>
  <div>
    <label for="">標題:</label>
    <input type="text">
  </div>
  <div>
    <label for="">內容:</label>
    <input type="text">
  </div>
  <div>
    <label for="">價錢:</label>
    <input type="number">
  </div>
  <input type="submit">
</form>

但是一般HTML會出現一個問題,Rails會檢查你的資料有沒有帶token,有才會讓你進來。
這時候我們可以用form_with方法,form_with算比較新的方法,他是form_for方法跟form_tag的結合版。

form_for後面加model,form_tag後面加url
而form_with則是model跟url都可以使用。

<%= form_with model:   , url:    do |form|%>

model跟url可以擇一使用,或是兩者都寫。
|form|是參數,名字可以隨意設定,不過命名請不要亂寫。

<h1>新增</h1>
    
<%= form_with model:Book.new do |form| %>

  <div>標題:<%= form.text_field :title %></div>
  <div>內容:<%= form.text_area :content %></div>
  <div>價錢:<%= form.text_field :price %></div>
  <%= form.submit "新增商品"%>
  
<% end %>

但是這樣寫有個問題,model:Book.new,Book代表著Book的model,在前端的地方盡量不要把方法寫在view,我們可以把Book.new移到controller

class BooksController < ApplicationController
    
  def index
  end
  
  def new
    @book = Book.new
  end
    
end

Rails設定上action會render他的action.html.erb,
所以他的實體變數view也能使用。

new.html.erb:

<%= form_with model:@book do |form| %>

Create

當我們輸入按下新增商品,會看到沒有create這個function

create這個動作是創造,因此不用特別去view寫一個頁面,而是要讓他創造成功後就馬上轉到其他網址。

那這邊可以輸入render html: params 來去觀看我們寫了什麼

class BooksController < ApplicationController
    
  def index
  end
  
  def new
    @book = Book.new
  end
    
  def create
    render html: params
  end
    
end


可以看到的是一包像hash的東西,因此我們可以在params後面加個找key的寫法[:book]

def create
    render html: params[:book]
  end


那接下來就是把這包資料,透過model存入資料庫。

class BooksController < ApplicationController
    
  def create
    @book = Book.new(params[:book])
  end
      
end

這時候按下新增商品會看到新的錯誤資訊

這個 ActiveModel::ForbiddenAttributesError 錯誤訊息發⽣的原因,是因為我們試圖把 params[:book] 裡的資料⼀⼝氣塞進 Model 裡。

這樣做會有安全上的問題,有⼼⼈⼠可以透過這個⽅式直接覆寫資料表裡某個欄位的值⽽取得特別權限或修改原本不應該被修改的欄位資料。

Rails有一套Strong Parameters 的做法,讓你可以對這包 params進⾏「清洗」或「過濾,只有寫在permit的東西才會進到裡面

class BooksController < ApplicationController
    
  def create
    book_params = params.require(:book).permit(:title,:content,:price)
    @book = Book.new(book_params )
  end
      
end

但這樣寫有點太長了,而且清洗資料這個動作不只有create會用的上,因此我們可以把這段寫成一個方法,然後再前面加上private讓這個方法只有在這個controller才可以使用

class BooksController < ApplicationController
    
  def create
    @book = Book.new(book_params)
  end

  private

  def book_params
    params.require(:book).permit(:title,:content,:price)
  end
      
end

那這個時候Book.new(book_params)會把這包清洗的資料先放在暫時記憶上面,那想要存入資料庫還需要用方法把他存進去。我們可以用save這個方法來達到我們的目的

class BooksController < ApplicationController
    
  def create
    @book = Book.new(book_params)
    @book.save
  end
      
end

再加一個if else判斷給他,如果存入成功就把網址轉到localhost:3000/books,失敗就轉到localhost:3000/books/new。

我們可以用redirect_to這個方法來轉網址

class BooksController < ApplicationController
    
  def create
    @book = Book.new(book_params)
    if @book.save
      redirect_to books_path
      # books_path 也可以寫 "/books" 也就是index的位置
    else
      redirect_to new_book_path
      # new_book_path 也可以寫  "/books/new" 也就是new的位置
    end
  end
      
end

那什麼會有失敗,是因為我們希望能對使用者輸入的東西做出一些的限制,例如title不能是空欄。這個動作我們稱它為驗證(Validations)
那驗證這個方法是在model進行,我們到app/models/boos.rb

class Book < ApplicationRecord
    
  validates :title, presence: true
    
end

presence: true 代表著title寫的東西一定要存在。

回到我們的瀏覽器,這時候title如果不寫東西就送出,會回到new。

但是這寫法,回來後寫的東西就不見了,這樣對使用者不方便,如果今天他寫的東西有一百項怎麼辦?使用者看到都是空的,不爽就不會想繼續操作了。

因此這裡我們會使用render這個寫法去做渲染

class BooksController < ApplicationController
    
  def create
    @book = Book.new(book_params)
    if @book.save
      redirect_to books_path
      # books_path 也可以寫 "/books" 也就是index的位置
    else
      render "/books/new"
    end
  end
      
end

要注意的是render後面寫的"/books/new",其實是views/books/new.html.erb

render做的事情是選一個html.erb頁面對我們的瀏覽器畫面做渲染,所以我們網址並沒有改,只是畫面變成new的畫面。

因為我們要渲染的new.html.erb在這個controller有其對應的action,所以我們可以這樣寫:

 def create
    @book = Book.new(book_params)
    if @book.save
      redirect_to books_path
      # books_path 也可以寫 "/books" 也就是index的位置
    else
      render :new
    end
  end

這時候如果title不輸入,內容跟價錢寫的東西都還在

title有輸入就會回到index畫面

我們到development.splite3可以看到我們剛剛輸入的資料


上一篇
Rails 30天挑戰第三天 CRUD(一)
下一篇
Rails 30天挑戰第五天 CRUD(三)
系列文
30天 Ruby on Rails 挑戰6
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言